home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / sound / aiff writer sdev / hardware.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  23.3 KB  |  783 lines

  1. /*
  2.     File:        Hardware.c
  3.  
  4.     Contains:    Routines for dealing with virtual sound hardware from a 'sdev' component.
  5.  
  6.     Written by: Mark Cookson--based on code by Kip Olson
  7.  
  8.     Copyright:    Copyright © 1993-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 8/16/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24. #ifndef __HARDWARE__
  25. #include "Hardware.h"
  26. #endif
  27. #include <Devices.h>
  28. #define DEBUG false
  29.  
  30. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  31. // Initialize the hardware. 
  32.  
  33. OSErr SetupHardware(SoundComponentGlobalsPtr globals) {
  34.     HardwareGlobalsPtr    hwGlobals    = globals->hwGlobals;
  35.     unsigned long        samples;
  36.     OSErr                result        = noErr;
  37.     
  38. #if DEBUG
  39. DebugStr ("\pIn SetupHardware");
  40. #endif
  41.  
  42.     // Setup hardware here. This example just calculates the interrupt interval
  43.     // for the time manager task interrupt based on the current sample rate.
  44.     
  45.     globals->interruptInterval = UnsignedFixedMulDiv(((long)kHardwareBufferSize) << 16, 1000000, hwGlobals->sampleRate);
  46.  
  47.     samples = UnsignedFixedMulDiv(hwGlobals->sampleRate, kSecondsInIOBuffer, kFix1);
  48.     samples *= (hwGlobals->sampleSize >> 3) * hwGlobals->numChannels;
  49.     globals->ioBufferSize = (samples + kHardwareBufferSize - 1) & (~(kHardwareBufferSize - 1));
  50.  
  51.     globals->thisSifter.flags = 0;
  52.     globals->thisSifter.format = (hwGlobals->sampleSize == 8) ? kOffsetBinary : kTwosComplement;    // set to hardware defaults
  53.     globals->thisSifter.sampleRate = hwGlobals->sampleRate;
  54.     globals->thisSifter.sampleSize = hwGlobals->sampleSize;
  55.     globals->thisSifter.numChannels = hwGlobals->numChannels;
  56.     globals->thisSifter.sampleCount = hwGlobals->bufferSize;
  57.  
  58.     if (globals->hwGlobals->outputToFile) {
  59.         result = SetupOutputFile(globals);                        // initialize output file
  60.     }
  61.  
  62. #if DEBUG
  63.     if (result != noErr) {
  64.         DebugStr ("\pGot an error in SetupHardware");
  65.     }
  66. #endif
  67.  
  68.     return (result);
  69. }
  70.  
  71. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  72. // Release the hardware. 
  73. //BUG ALERT!  If we sample MicroSeconds at just the wrong time, we could be here for a long time!
  74. void ReleaseHardware(SoundComponentGlobalsPtr globals) {
  75.     unsigned long        timeLimit, startTime;
  76.  
  77. #if DEBUG
  78. DebugStr ("\pIn ReleaseHardware");
  79. #endif
  80.  
  81.     startTime = MicroSeconds();
  82.     timeLimit = globals->interruptInterval << 1;                // wait for 2 interrupt periods
  83.     while ((globals->hardwareOn) &&                                // wait until interrupts turn off
  84.            ((MicroSeconds() - startTime) < timeLimit)) {        // or we time out
  85.         ;
  86.     }
  87.  
  88.     StopHardware(globals);                                        // make sure hardware is off
  89.  
  90.     if (globals->fRefNum) {
  91.         CloseOutputFile(globals);                                // close output file
  92.         globals->fRefNum = 0;
  93.     }
  94. }
  95.  
  96. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  97. // Turn on hardware interrupts.
  98.  
  99. void StartHardware(SoundComponentGlobalsPtr globals) {
  100. #if DEBUG
  101. DebugStr ("\pIn StartHardware");
  102. #endif
  103.  
  104.     if (!globals->hardwareOn) {
  105.         globals->hardwareOn = true;                                // the hardware will soon be on
  106.  
  107.         // Turn hardware on here. The example uses the time manager for interrupts
  108.  
  109. #ifndef FakeInterrupts
  110.         // start the time manager task going
  111.  
  112.         globals->tmTask.task.tmAddr = NewTimerProc (TMInterrupt);
  113.         globals->tmTask.task.tmCount = 0;
  114.         globals->tmTask.task.tmWakeUp = 0;
  115.         globals->tmTask.task.tmReserved = 0;
  116.         globals->tmTask.globals = globals;
  117.  
  118.         InsXTime((QElemPtr) &globals->tmTask);
  119.         PrimeTime((QElemPtr) &globals->tmTask, -globals->interruptInterval);
  120. #endif
  121.     }
  122. }
  123.  
  124. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  125. // Turn off hardware interrupts.
  126.  
  127. void StopHardware(SoundComponentGlobalsPtr globals) {
  128. #if DEBUG
  129. DebugStr ("\pIn StopHardware");
  130. #endif
  131.  
  132.     if (globals->hardwareOn) {
  133.         // Turn hardware off here. The example removes the time manager task
  134.  
  135. #ifndef FakeInterrupts
  136.         //Dispose of the Time Manager task UPP made in StartHardware
  137.         RmvTime((QElemPtr) &globals->tmTask);
  138.         DisposeRoutineDescriptor (globals->tmTask.task.tmAddr);
  139. #endif
  140.         globals->hardwareOn = false;                            // the hardware is now off
  141.     }
  142. }
  143.  
  144. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  145. // Temporarily suspend hardware interrupts, so the interrupt routine can work in peace.
  146.  
  147. void SuspendHardware(SoundComponentGlobalsPtr globals) {
  148. #if DEBUG
  149. DebugStr ("\pIn SuspendHardware");
  150. #endif
  151.  
  152.     if (globals->hardwareOn) {
  153.         // Suspend hardware interrupts here
  154.     }
  155. }
  156.  
  157. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  158. // Resume hardware interrupts after they were suspended.
  159.  
  160. void ResumeHardware(SoundComponentGlobalsPtr globals) {
  161. #if DEBUG
  162. DebugStr ("\pIn ResumeHardware");
  163. #endif
  164.  
  165.     if (globals->hardwareOn) {
  166.         // Resume hardware interrupts here. The example queues another time manager interrupt
  167.  
  168. #ifndef FakeInterrupts
  169.         PrimeTime((QElemPtr) &globals->tmTask, -globals->interruptInterval);
  170. #endif
  171.     }
  172. }
  173.  
  174. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  175. // Copy samples to the hardware buffer. In our case, write to a file.
  176.  
  177. #define SamplesToBytes(samples, shift)    (samples << shift)
  178. #define BytesToSamples(bytes, shift)    (bytes >> shift)
  179.  
  180. void CopySamplesToHardware(SoundComponentGlobalsPtr globals, SoundComponentDataPtr siftPtr) {
  181.     long            samplesToCopy, bytesToCopy, bytesLeft;
  182.     IOBufferPtr        currentBuffer;
  183.     short            sourceShift, destShift;
  184.  
  185. #if DEBUG
  186. DebugStr ("\pIn CopySamplesToHardware");
  187. #endif
  188.  
  189.     sourceShift = 0;
  190.     if (siftPtr->numChannels == 2) {
  191.         sourceShift++;
  192.     }
  193.     if (siftPtr->sampleSize == 16) {
  194.         sourceShift++;
  195.     }
  196.  
  197.     destShift = 0;
  198.     if (globals->thisSifter.numChannels == 2) {
  199.         destShift++;
  200.     }
  201.     if (globals->thisSifter.sampleSize == 16) {
  202.         destShift++;
  203.     }
  204.  
  205.     samplesToCopy = siftPtr->sampleCount;                        // don't copy more than hardware buffer has
  206.     if (samplesToCopy > kHardwareBufferSize) {
  207.         samplesToCopy = kHardwareBufferSize;
  208.     }
  209.  
  210.     currentBuffer = &globals->ioBuffers[globals->currentIndex];        // get current i/o buffer
  211.     if (currentBuffer->iopb.ioParam.ioResult == 0) {
  212.         bytesToCopy = SamplesToBytes(samplesToCopy, destShift);        // turn samples into bytes
  213.         bytesLeft = globals->ioBufferSize - currentBuffer->byteCount;        // calc how much is left in i/o buffer
  214.  
  215.         if (bytesToCopy > bytesLeft) {                                // limit to amount we have left in buffer
  216.             bytesToCopy = bytesLeft;
  217.         }
  218.         samplesToCopy = BytesToSamples(bytesToCopy, destShift);        // turn back into samples
  219.  
  220.         if (globals->fRefNum) {                                        // only write out if file is open
  221.             OutputToFile(globals, siftPtr, currentBuffer->buffer + currentBuffer->byteCount, samplesToCopy);
  222.         }
  223.  
  224.         currentBuffer->byteCount += bytesToCopy;                    // update dest pointer
  225.         siftPtr->buffer += SamplesToBytes(samplesToCopy, sourceShift);    // update source pointer
  226.         siftPtr->sampleCount -= samplesToCopy;                        // subtract amount copied from source
  227.         
  228.         if (currentBuffer->byteCount == globals->ioBufferSize) {    // current buffer is full - write it out
  229.             if (globals->fRefNum) {                                    // only write out if file is open
  230.                 currentBuffer->iopb.ioParam.ioReqCount = currentBuffer->byteCount;    // write this many bytes out
  231.                 currentBuffer->iopb.ioParam.ioPosOffset = 0;        // offset from current position
  232.                 
  233.     #ifdef FakeInterrupts
  234.                 PBWriteSync(¤tBuffer->iopb);                    // write out the data synchronously
  235.     #else
  236.                 PBWriteAsync(¤tBuffer->iopb);                    // start the asynch write
  237.     #endif
  238.             }
  239.  
  240.             currentBuffer->byteCount = 0;                            // buffer will be empty next time we use it
  241.             globals->currentIndex ^= 1;                                // switch to other buffer
  242.         }
  243.     } else {                                                        // oh, oh, write is still in progress
  244.         siftPtr->buffer += SamplesToBytes(samplesToCopy, sourceShift);    // update source pointer
  245.         siftPtr->sampleCount -= samplesToCopy;                        // subtract amount copied from source and throw these bytes away!
  246.     }
  247. }
  248.  
  249. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  250. // Slam the file.
  251.  
  252. void OutputToFile(SoundComponentGlobalsPtr globals, SoundComponentDataPtr siftPtr, void *dest, short sampleCount) {
  253.     short    i;
  254.  
  255. #if DEBUG
  256. DebugStr ("\pIn OutputToFile");
  257. #endif
  258.  
  259.     if (siftPtr->sampleSize == 8) {
  260.         Byte    *sp = siftPtr->buffer;
  261.         Byte    *dp = dest;
  262.  
  263.         if (siftPtr->numChannels == 1) {
  264.             if (globals->thisSifter.numChannels == 1) {
  265.                 for (i = sampleCount - 1; i >= 0; --i) {
  266.                     *dp++ = *sp++ ^ 0x80;
  267.                 }
  268.             } else {
  269.                 for (i = sampleCount - 1; i >= 0; --i) {
  270.                     *dp++ = *sp ^ 0x80;
  271.                     *dp++ = *sp++ ^ 0x80;
  272.                 }
  273.             }
  274.         } else {
  275.             for (i = sampleCount - 1; i >= 0; --i) {
  276.                 *dp++ = *sp++ ^ 0x80;
  277.                 *dp++ = *sp++ ^ 0x80;
  278.             }
  279.         }
  280.     } else {
  281.         short    *sp = (short *) siftPtr->buffer;
  282.         short    *dp = dest;
  283.  
  284.         if (siftPtr->numChannels == 1) {
  285.             for (i = sampleCount - 1; i >= 0; --i) {
  286.                 *dp++ = *sp++;
  287.             }
  288.         } else {
  289.             for (i = sampleCount - 1; i >= 0; --i) {
  290.                 *dp++ = *sp++;
  291.                 *dp++ = *sp++;
  292.             }
  293.         }
  294.     }
  295. }
  296.  
  297. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  298. // Fake interrupt routine used when running Think C to avoid the time manager.
  299.  
  300. #ifdef FakeInterrupts
  301. Boolean FakeInterrupt(void) {
  302.     SoundComponentGlobalsPtr    globals = gGlobals;                    // get our globals
  303.  
  304. #if DEBUG
  305. DebugStr ("\pIn FakeInterrupt");
  306. #endif
  307.  
  308.     if (globals->hardwareOn) {
  309.         if (MicroSeconds() >= globals->nextTime) {
  310.             globals->nextTime = MicroSeconds();
  311.             InterruptRoutine(globals);
  312.             globals->nextTime += globals->interruptInterval;
  313.         }
  314.     }
  315.  
  316.     return (globals->hardwareOn);
  317. }
  318. #else
  319.  
  320. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  321. // Entry point for the time manager task interrupt routine
  322.  
  323. //void TMInterrupt(void)
  324. #if !GENERATINGPOWERPC
  325. void TMInterrupt(myTMTaskPtr taskPtr:__a1)
  326. #else
  327. void TMInterrupt(myTMTaskPtr taskPtr)
  328. #endif
  329. {
  330. #if DEBUG
  331. DebugStr ("\pIn TMInterrupt");
  332. #endif
  333.  
  334.     //This next line needed only in Think C, not MetroWerks
  335.     //myTMTaskPtr taskPtr = (myTMTaskPtr) GetRegisterA1();    // get poiner to time manager param block
  336.     
  337.     InterruptRoutine(taskPtr->globals);                        // call interrupt routine with globals
  338. }
  339. #endif
  340.  
  341. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  342. // This routine is called every hardware interrupt to fill the hardware
  343. // with audio data. First it should suspend interrupts so it will not
  344. // be interrupted again (this example just queues up the next interrupt).
  345. // Then it should get more data from the source mixer, and copy the data
  346. // to the hardware. In the way out, if all data was copied, try to get
  347. // some more so it will be available next time.
  348.  
  349. void InterruptRoutine(SoundComponentGlobalsPtr globals) {
  350.     SoundComponentDataPtr        siftPtr;
  351.  
  352. #if DEBUG
  353. DebugStr ("\pIn InterruptRoutine");
  354. #endif
  355.  
  356.     // SuspendHardware(globals);                            // suspend interrupt while we are processing
  357.     ResumeHardware(globals);                                // queue next interrupt while we are processing
  358.  
  359.     siftPtr = GetMoreSource(globals);                        // get source from mixer
  360.     if (siftPtr != nil) {
  361.         CopySamplesToHardware(globals, siftPtr);            // fullfill hardware request
  362.  
  363.         if (siftPtr->sampleCount == 0) {                    // exhausted the source
  364.             siftPtr = GetMoreSource(globals);                // get more for next time
  365.         }
  366.     // ResumeHardware(globals);                                // resume interrupts
  367.     } else {                                                // no more source
  368.         StopHardware(globals);                                // turn hardware off
  369.     }
  370.  
  371.     return;
  372. }
  373.  
  374. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  375. // This routine returns the component data pointer to your mixer source. If there
  376. // is no source or it is empty, it will call down the chain to fill it up.
  377.  
  378. SoundComponentDataPtr GetMoreSource(SoundComponentGlobalsPtr globals) {
  379.     ComponentResult            result;
  380.     SoundComponentDataPtr    siftPtr = globals->sourceDataPtr;
  381.  
  382. #if DEBUG
  383. DebugStr ("\pIn GetMoreSource");
  384. #endif
  385.  
  386.     if ((siftPtr == nil) || (siftPtr->sampleCount == 0)) {    // no data - better get some
  387.         result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
  388.         siftPtr = globals->sourceDataPtr;
  389.  
  390.         if ((result != noErr) ||                            // error getting data
  391.             (siftPtr == nil) ||                                // source has no data pointer to return
  392.             (siftPtr->sampleCount == 0)) {                    // source has no more data
  393. #if DEBUG
  394.                 DebugStr ("\p we have no more data");
  395. #endif
  396.                 siftPtr = nil;
  397.         }
  398.     }
  399.     
  400.     return (siftPtr);                                        // return pointer to source
  401. }
  402.  
  403. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  404. // This routine initializes the output file and sets up the i/o param block
  405. // used to write the data to disk.
  406.  
  407. OSErr SetupOutputFile(SoundComponentGlobalsPtr globals) {
  408.     FSSpec            fspec;
  409.     ParmBlkPtr        iopb;
  410.     int                i, j;
  411.     char            *sp;
  412.     IOBufferPtr        ioBuffer;
  413.     OSErr            err;
  414.  
  415. #if DEBUG
  416. DebugStr ("\pIn SetupOutputFile");
  417. #endif
  418.  
  419.     err = FSMakeFSSpec(0, 0, kSndFileName, &fspec);                // make the FS spec
  420.  
  421.     if (err == noErr) {                                            // file exists
  422.         err = FSpDelete(&fspec);                                // delete it
  423.         if (err != noErr) {
  424.             DebugStr("\pCould not delete file");
  425.         }
  426.     } else if (err != fnfErr) {
  427.         DebugStr("\pCould not make FSSpec");
  428.     } else {
  429.         err = noErr;        // file-not-found is not a real error since we're creating the file
  430.     }
  431.  
  432.     if (err == noErr) {
  433.         err = FSpCreate(&fspec, kCreator, kFileType, smSystemScript);    // create file
  434.         if (err != noErr) {
  435.             DebugStr("\pCould not create file");
  436.         }
  437.     }
  438.  
  439.     if (err == noErr) {
  440.         err = FSpOpenDF(&fspec, fsRdWrPerm, &globals->fRefNum);        // open file
  441.  
  442.         if (err != noErr) {
  443.             DebugStr("\pCould not open file");
  444.         }
  445.     }
  446.  
  447.     if (err == noErr) {
  448.         err = SetupAIFFHeader(globals->fRefNum, globals->thisSifter.numChannels, globals->thisSifter.sampleRate,
  449.                               globals->thisSifter.sampleSize, 'NONE', 0, 0);
  450.         if (err != noErr) {
  451.             DebugStr("\pError in SetupAIFFHeader");
  452.         }
  453.     }
  454.  
  455.     if (err == noErr) {
  456.         err = GetFPos(globals->fRefNum, &globals->headerLen);        // get length of AIFF header
  457.         if (err != noErr) {
  458.             DebugStr("\pCould not get file position");
  459.         }
  460.     }
  461.  
  462.     if (err == noErr) {
  463.         for (i = 0; i < 2; ++i) {                                    // loop over both parameter blocks
  464.             ioBuffer = &globals->ioBuffers[i];
  465.  
  466.             // get some memory for i/o buffers
  467.             ioBuffer->bufferHandle = NewHandleLockClear(globals->ioBufferSize, globals->inSystemHeap);
  468.             if (ioBuffer->bufferHandle == nil) {
  469.                 err = MemError();
  470.                 DebugStr("\pCould not get NewHandleLockClear");
  471.                 break;        // get out of for loop with error
  472.             }
  473.  
  474.             ioBuffer->buffer = *ioBuffer->bufferHandle;                // get pointer to buffer
  475.  
  476.             iopb = &ioBuffer->iopb;                                    // get pointer to param block
  477.  
  478.             sp = (char *) iopb;
  479.             for (j = sizeof(ParamBlockRec) - 1; j >= 0; --j) {        // zero-out the param block
  480.                 *sp++ = 0;
  481.             }
  482.  
  483.             /* fill up the param block with the fields that never change after this. Note that
  484.                fields that are set to zero are commented out since we just zeroed things above. */
  485.  
  486.             iopb->ioParam.ioRefNum        = globals->fRefNum;            // refnum of file to write to
  487.     //        iopb->ioParam.ioCompletion    = nil;                        // no completion routine
  488.     //        iopb->ioParam.ioReqCount    = 0;                        // no request yet
  489.             iopb->ioParam.ioBuffer        = ioBuffer->buffer;            // buffer to write to
  490.             iopb->ioParam.ioPosMode        = fsAtMark | fsNoCache;        // write from current position w/o cache
  491.     //        iopb->ioParam.ioPosOffset    = 0;                        // no offset yet
  492.         }
  493.     }
  494.  
  495.     if (err != noErr) {
  496.         if (globals->fRefNum) {
  497.             FSClose(globals->fRefNum);
  498.         }
  499.  
  500.         DisposeHandle(globals->ioBuffers[0].bufferHandle);            // dispose of buffers
  501.         DisposeHandle(globals->ioBuffers[1].bufferHandle);
  502.     }
  503.         
  504. #if DEBUG
  505.     if (err != noErr) {
  506.         DebugStr ("\pGot an error in SetupOutputFile");
  507.     }
  508. #endif
  509.  
  510.     return (err);
  511. }
  512.  
  513. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  514. // This routine makes sure all asynchronous i/o is complete, writes out any
  515. // remaining data, updates the AIFF header and closes the file.
  516.  
  517. OSErr CloseOutputFile(SoundComponentGlobalsPtr globals) {
  518.     long            filePos, bytesWritten;
  519.     IOBufferPtr        currentBuffer;
  520.     OSErr            err;
  521.  
  522. #if DEBUG
  523. DebugStr ("\pIn CloseOutputFile");
  524. #endif
  525.  
  526.     while  ((globals->ioBuffers[globals->currentIndex].iopb.ioParam.ioResult > 0) ||    // wait for PBWrite to complete
  527.             (globals->ioBuffers[globals->currentIndex^1].iopb.ioParam.ioResult > 0)) {    // for both buffers
  528.         ;
  529.     }
  530.  
  531.     currentBuffer = &globals->ioBuffers[globals->currentIndex];    // get current i/o buffer
  532.  
  533.     if (currentBuffer->byteCount != 0) {                // still some data to write out
  534.         currentBuffer->iopb.ioParam.ioReqCount = currentBuffer->byteCount;    // write this many bytes out
  535.         currentBuffer->iopb.ioParam.ioPosOffset = 0;    // offset from current position
  536.  
  537.         PBWriteSync(¤tBuffer->iopb);                // write this buffer synchronously
  538.     }
  539.  
  540.     err = GetFPos(globals->fRefNum, &filePos);            // get current file position
  541.     if (err != noErr) {
  542.         DebugStr("\pCould not get file position");
  543.     }
  544.  
  545.     if (err == noErr) {
  546.         bytesWritten = filePos - globals->headerLen;        // calc no. bytes written to file
  547.         filePos = ++filePos & ~1;                            // make sure file length is a word-aligned
  548.  
  549.         err = SetEOF(globals->fRefNum, filePos);            // set current file position to EOF
  550.         if (err != noErr) {
  551.             DebugStr("\pCould not set EOF");
  552.         }
  553.     }
  554.  
  555.     if (err == noErr) {
  556.         err = SetFPos(globals->fRefNum, fsFromStart, 0);    // rewind file to beginning
  557.         if (err != noErr) {
  558.             DebugStr("\pCould not set file position");
  559.         }
  560.     }
  561.  
  562.     if (err == noErr) {
  563.         err = SetupAIFFHeader(globals->fRefNum, globals->thisSifter.numChannels, globals->thisSifter.sampleRate,
  564.                               globals->thisSifter.sampleSize, 'NONE', bytesWritten, 0);
  565.         if (err != noErr) {
  566.             DebugStr("\pError in SetupAIFFHeader");
  567.         }
  568.     }
  569.  
  570.     FSClose(globals->fRefNum);                            // close output file
  571.     globals->fRefNum = 0;
  572.  
  573.     DisposeHandle(globals->ioBuffers[0].bufferHandle);    // dispose of buffers
  574.     DisposeHandle(globals->ioBuffers[1].bufferHandle);
  575.  
  576. #if DEBUG
  577.     if (err != noErr) {
  578.         DebugStr ("\pGot an error in CloseOutputFile");
  579.     }
  580. #endif
  581.  
  582.     return (err);
  583. }
  584.  
  585. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  586. // This routine creates a locked handle in the requested heap.
  587.  
  588. Handle NewHandleLockClear(long len, Boolean inSystemHeap) {    // allocate a new handle, lock it down and clear it
  589.     Handle        h;
  590.  
  591. #if FULLDEBUG
  592. DebugStr ("\pIn NewHandleLockClear");
  593. #endif
  594.  
  595.     if (inSystemHeap) {                                        // we are loaded into the system heap
  596.         ReserveMemSys(len);                                    // create it low down in system heap
  597.         h = NewHandleSysClear(len);
  598.     } else {                                                // we are loaded into the app heap
  599.         h = NewHandleClear(len);                            // create our memory there and move it out of the way
  600.         if (h != nil) {
  601.             MoveHHi(h);
  602.         }
  603.     }
  604.  
  605.     if (h != nil) {
  606.         HLock(h);                                        // lock it down
  607.     }
  608.  
  609.     return (h);
  610. }
  611.  
  612. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  613. // This routine gets the hardware globals out of the refCon or creates them
  614.  
  615. HardwareGlobalsPtr GetHardwareGlobals(ComponentInstance self, Boolean inSystemHeap) {
  616.     HardwareGlobalsPtr    hwGlobals;
  617.     Handle                h, componentName;
  618.     UnsignedFixed        *lp;
  619.     short                *sp, i;
  620.     ComponentDescription componentDesc;
  621.     OSErr                err;
  622.  
  623. #if DEBUG
  624. DebugStr ("\pIn GetHardwareGlobals");
  625. #endif
  626.  
  627.     hwGlobals = (HardwareGlobalsPtr) GetComponentRefcon((Component) self);
  628.     if (hwGlobals == nil) {        //have to set up the hardware globals
  629.         componentName = NewHandle(0);
  630.         if (componentName != nil) {
  631.             err = GetComponentInfo((Component) self, &componentDesc, componentName, nil, nil);
  632.             if (err == noErr) {
  633.                 h = NewHandleLockClear(sizeof(HardwareGlobals), inSystemHeap);
  634.                 if (h != nil) {
  635.                     HUnlock(h);
  636.                     HLock(componentName);
  637.                     err = GetSoundPreference(componentDesc.componentSubType, (StringPtr) *componentName, h);
  638.                     if (err == noErr) {
  639.                         DisposeHandle(componentName);
  640.                         HLock(h);
  641.                         hwGlobals = (HardwareGlobalsPtr) *h;
  642.                         SetComponentRefcon((Component) self, (long) hwGlobals);
  643.                     } else {
  644.                         DisposeHandle(h);        //GetSoundPreference failed
  645.                         DisposeHandle(componentName);
  646.                     }
  647.                 } else {                        //NewHandleLockClear failed
  648.                     DisposeHandle(componentName);
  649.                 }
  650.             } else {                            //GetComponentInfo failed
  651.                 DisposeHandle(componentName);
  652.             }
  653.  
  654.             if (h == nil || err != noErr) {        //failed to get globals, try to make them
  655.                 h = NewHandleLockClear(sizeof(HardwareGlobals), inSystemHeap);
  656.                 if (h != nil) {
  657.                     hwGlobals = (HardwareGlobalsPtr) *h;
  658.  
  659.                     SetComponentRefcon((Component) self, (long) hwGlobals);
  660.  
  661.                     /* Setup sample sizes */
  662.                     
  663.                     sp = hwGlobals->sampleSizesActive;
  664.                     for (i = kSampleSizesCount - 1; i >= 0; --i) {
  665.                         *sp++ = 1;
  666.                     }
  667.                     sp = hwGlobals->sampleSizes;
  668.                     *sp++ = 8;                                // 8-bit
  669.                     *sp++ = 16;                                // 16-bit
  670.  
  671.                     /* Setup sample rates */
  672.                     
  673.                     sp = hwGlobals->sampleRatesActive;
  674.                     for (i = kSampleRatesCount - 1; i >= 0; --i) {
  675.                         *sp++ = 1;
  676.                     }
  677.                     lp = hwGlobals->sampleRates;
  678.                     *lp++ = rate44khz;                        // 44.100 kHz        
  679.                     *lp++ = rate22050hz;                    // 22.050 kHz
  680.                     *lp++ = rate11025hz;                    // 11.025 kHz
  681.                     *lp++ = rate22khz;                        // 22.254 kHz
  682.                     *lp++ = rate11khz;                        // 11.127 kHz
  683.                     *lp++ = 0x1F400000;                        //  8.000 kHz
  684.  
  685.                     hwGlobals->sampleRateMin = 0x00010000;    // sample rate min
  686.                     hwGlobals->sampleRateMax = 0xAC440000;    // sample rate max
  687.  
  688.                     /* Setup channels */
  689.  
  690.                     sp = hwGlobals->channelsActive;
  691.                     for (i = kChannelsCount - 1; i >= 0; --i) {
  692.                         *sp++ = 1;
  693.                     }
  694.                     sp = hwGlobals->channels;
  695.                     *sp++ = 1;                                // mono
  696.                     *sp++ = 2;                                // stereo
  697.  
  698.                     /* Initial hardware settings */
  699.                     
  700.                     hwGlobals->volume = ((long)256 << 16) | 256;
  701.                     hwGlobals->sampleSize = 16;
  702.                     hwGlobals->numChannels = 2;
  703.                     hwGlobals->sampleRate = rate44khz;
  704.                     hwGlobals->bufferSize = 1024;
  705.                     hwGlobals->supportsRateRange = false;
  706.                     hwGlobals->outputToFile = true;
  707.                     hwGlobals->outputToHardware = false;
  708.                     hwGlobals->dirty = true;
  709.                 } else {
  710.                     hwGlobals = nil;
  711.                 }
  712.             }
  713.         } else {        //NewHandle(0) failed, no memory for globals
  714.             hwGlobals = nil;
  715.         }
  716.     }
  717.  
  718.     return (hwGlobals);
  719. }
  720.  
  721. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  722. // This routine saves the hardware globals in the refCon
  723.  
  724. OSErr SaveHardwareGlobals(ComponentInstance self, HardwareGlobalsPtr hwGlobals) {
  725.     Handle                    h,
  726.                             componentName;
  727.     ComponentDescription    componentDesc;
  728.     OSErr                    err                = noErr;
  729.  
  730. #if DEBUG
  731. DebugStr ("\pIn SaveHardwareGlobals");
  732. #endif
  733.  
  734.     if (hwGlobals != nil) {
  735.         if (hwGlobals->dirty == true) {
  736.             componentName = NewHandle(0);
  737.             if (componentName != nil) {
  738.                 err = GetComponentInfo((Component) self, &componentDesc, componentName, nil, nil);
  739.                 if (err != noErr) {
  740.                     h = RecoverHandle((Ptr) hwGlobals);
  741.                     err = MemError();
  742.                     if (err != noErr) {
  743.                         HLock(componentName);
  744.                         hwGlobals->dirty = false;
  745.                         err = SetSoundPreference(componentDesc.componentSubType, (StringPtr) *componentName, h);
  746.                     } else {
  747.                         DisposeHandle(componentName);
  748.                     }
  749.                 } else {
  750.                     DisposeHandle(componentName);
  751.                 }
  752.             } else {
  753.                 err = MemError();
  754.             }
  755.         }
  756.     } else {
  757.         err = notEnoughHardwareErr;
  758.     }
  759.  
  760. #if DEBUG
  761.     if (err != noErr) {
  762.         DebugStr ("\pGot an error in SaveHardwareGlobals");
  763.     }
  764. #endif
  765.  
  766.     return (err);
  767. }
  768.  
  769. #if GENERATINGPOWERPC
  770. //for 68K the macro we are using only returns a long, so the PPC version
  771. //will just return the lo part of the number to be consistent
  772. unsigned long            MicroSeconds(void) {
  773.     UnsignedWide            microTickCount;
  774.  
  775. #if FULLDEBUG
  776. DebugStr ("\pIn MicroSeconds");
  777. #endif
  778.  
  779.     Microseconds (µTickCount);
  780.     return (microTickCount.lo);
  781. }
  782. #endif
  783.